"""
MythBoxLink - http://mythboxlink.googlecode.com
(c) 2011 Jared Sutton <jpsutton@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2 as
published by the Free Software Foundation. 

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Special Notes:
 - The levenshtein function is derived from http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#Python
     It is licensed under the 'Creative Commons Attribution-ShareAlike 3.0' (NOT covered by the above GPL notice)
"""

import re, MySQLdb, os, os.path, commands
from MythBoxLinkSettings import settings

debug = False

def levenshtein(s1, s2):
  if len(s1) < len(s2):
    return levenshtein(s2, s1)

  if not s1:
    return len(s2)
 
  previous_row = xrange(len(s2) + 1)

  for i, c1 in enumerate(s1):
    current_row = [i + 1]

    for j, c2 in enumerate(s2):
      insertions = previous_row[j + 1] + 1 # j+1 instead of j since previous_row and current_row are one character longer
      deletions = current_row[j] + 1       # than s2
      substitutions = previous_row[j] + (c1 != c2)
      current_row.append(min(insertions, deletions, substitutions))

    previous_row = current_row
 
  return previous_row[-1]

# A wrapper around os.makedirs that doesn't compain if the target directory already exists
def mkdir(directory):
  if debug:
    print("DEBUG: create directory '%s'" % directory)
  else:
    try:
      os.makedirs(directory)
    except:
      pass

# A wrapper around os.symlink that doesn't compain if the link already exists
def symlink(src, dest):
  if debug:
    print("DEBUG: create symlink '%s' -> '%s'" % (src, dest))
  else:
    try:
      os.symlink(src, dest)
    except:
      pass

# use mplayer to try to determine the frame rate of a given file; works for me with MPEG2- and H.264-encoded files in M2TS containers
def getFrameRate(filename):
  result = commands.getoutput('mplayer -nosound -vo null -endpos 1 "%s" 2>/dev/null | pcregrep "^(FPS|VIDEO:  MPEG2)"' % filename)
  rate = 29.970

  if result.startswith('FPS'):
    rate = float(result.replace("FPS seems to be: ", ""))
  elif result.startswith('VIDEO:  MPEG2'):
    rate = float(re.search("([0-9]+\.[0-9]+ fps)", result).group(0).split(" ")[0])

  return rate

# Construct a well-formed filename for recorded episodes
def formatFileName (epdata, title):
  se = str(epdata[4])

  if len(se) < 2:
    se = "0" + se

  ep = str(epdata[5])

  if len(ep) < 2:
    ep = "0" + ep

  return ('%s.S%sE%s%s' % (title, se, ep,'.HDTV')).encode('utf-8')

# Generate an EDL-compatible commercial-skip file for a recording
def generateCommSkipFile (startTime, channelID, fileName, fps):
  conn = MySQLdb.connect(host = settings.dbhost, user = settings.dbuser, passwd = settings.dbpass, db = settings.dbname, use_unicode = True)
  cursor = conn.cursor()

  if channelID not in settings.ignoreCommSkipList:
    strCommStartSelect = "select mark from recordedmarkup where starttime = '%s' and chanid = %s and type = 4" % (startTime, channelID)
    strCommStopSelect = "select mark from recordedmarkup where starttime = '%s' and chanid = %s and type = 5" % (startTime, channelID)
    cursor.execute(strCommStartSelect)
    commStart = cursor.fetchall()
    cursor.execute(strCommStopSelect)
    commStop = cursor.fetchall()
    commSkipData = []

    for i in range(0, len(commStart)):
      startSecond = int(int(commStart[i][0]) / fps) + 1
      stopSecond = int(int(commStop[i][0]) / fps) - 1
      commSkipData.append("%s   %s      3\n" % (startSecond, stopSecond))

    #commSkipFile = (os.path.join(destFolder, movie[1] + '.edl')).encode('utf-8')
    commSkipFile = fileName.replace('.m2ts', '.edl')
    commSkipOut = open(commSkipFile, "w")
    commSkipOut.writelines(commSkipData)
    commSkipOut.close()

def getCurrentLinks (startingDir):
  # First things first: clean up broken symlinks (deleted recordings) and build list of already-indexed stuff
  directories = [ startingDir ]
  currentLinks = list()

  while len(directories) > 0:
    directory = directories.pop()

    for name in os.listdir(directory):
      fullpath = os.path.join(directory,name)

      if os.path.islink(fullpath):
        if os.path.exists(fullpath) and (os.path.dirname(fullpath).endswith("Movies") or len(os.path.basename(fullpath).split('.')) in (3, 4, 5)):
          # That condition is a little awkward.  It basically makes sure that the target file...
          #   A. exists (otherwise the previously-recorded file has been deleted, and the link can be removed)
          #   ...and either...
          #   B. It's in a folder called "Movies" (which have no filename formatting requirements)
          #   ...or...
          #   C. It's filename has 2 periods (which is one characteristic of a properly-formatted episode filename of "PROG_NAME.SXXEYY.EXT")
          currentLinks.append(os.path.basename(os.path.realpath(fullpath))) # It's a symlink, store the target filename to exclude it from database queries
        else:
          print "Removing dead/improper symlink %s (so we can force a re-check of it's meta-data)" % fullpath
          os.unlink(fullpath)

          try:
            os.unlink(fullpath.replace('.m2ts', '.edl'))
          except:
            pass
      elif os.path.isdir(fullpath):
        directories.append(fullpath)  # It's a directory, store it for recursion.
  return currentLinks

def getRecordingsFromMythDB (currentLinks):
  # Connect to the MythTV Database and get a list of stuff to index
  conn = MySQLdb.connect(host = settings.dbhost, user = settings.dbuser, passwd = settings.dbpass, db = settings.dbname, use_unicode = True)

  strMovieSelect = """select programid, title, subtitle, basename, storagegroup, starttime, chanid from recorded where commflagged = 1 and category != 'Adults only' and basename not in ('%s') and programid like 'MV%s' order by title, subtitle;""" % ("', '".join(currentLinks), "%")
  """
  Column order for Movies:
    1: programid
    2: title
    3: subtitle
    4: basename
    5: storagegroup
    6: starttime
    7: chanid
  """

  strEpisodeSelect = """select recorded.programid, recorded.title, recorded.subtitle, recorded.basename, recorded.originalairdate, recorded.storagegroup, recorded.starttime, recorded.chanid, recordedprogram.syndicatedepisodenumber from recorded left join recordedprogram on recorded.programid = recordedprogram.programid where recorded.commflagged = 1 and recorded.category != 'Adults only' and recorded.basename not in ('%s') and recorded.programid like 'EP%s' order by recorded.title, recorded.subtitle, recorded.originalairdate asc, recorded.starttime asc;""" % ("', '".join(currentLinks), "%")
  """
  Column order for Episodes:
    1: programid
    2: title
    3: subtitle
    4: basename
    5: originalairdate
    6: storagegroup
    7: starttime
    8: chanid
    9: syndicatedepisodenumber
  """


  cursor = conn.cursor()

  cursor.execute(strMovieSelect)
  movieRows = cursor.fetchall()

  cursor.execute(strEpisodeSelect)
  episodeRows = cursor.fetchall()

  return (movieRows, episodeRows)

def linkMovies (movieRows):
  # Process the movie results from the database
  for movie in movieRows:
    #print(movie)
    if movie[4] == "Default":
      subdir = "recordings"
    elif movie[4] == "LiveTV":
      subdir = "livetv"

    # Movies are easy...just symlink the file with a "TITLE.m2ts" filename
    srcFile = os.path.join(settings.recordingsDir, subdir, movie[3])
    destFolder = os.path.join(settings.outputDir, "Movies")
    mkdir(destFolder)
    destFile = (os.path.join(destFolder, movie[1] + '.HDTV.m2ts')).encode('utf-8')
    symlink(srcFile, destFile)
    fps = getFrameRate(srcFile)
    generateCommSkipFile(movie[5], movie[6], destFile, fps)

